/*
 * Created by JFormDesigner on Fri May 04 20:41:31 CEST 2007
 */

package info.rflow;

import java.beans.*;

import info.rflow.actions.AboutAction;
import info.rflow.actions.ExportImageAction;
import info.rflow.graph.RWorkflowScene;
import info.rflow.graph.model.RBlockNode;
import info.rflow.model.RBlock;
import info.rflow.model.RWorkflowModel;
import info.rflow.preferences.GeneralSettings;
import info.rflow.ui.BirdEyeViewDialog;
import info.rflow.ui.SourceArea;
import info.rflow.ui.StatusBar;
import info.rflow.util.*;
import org.netbeans.api.visual.widget.EventProcessingType;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.prefs.Preferences;


/**
 * @author Holger Brandl
 */
public class RFlow extends JFrame {

    public static final String RFLOW_NAME = "RFlow";

    private BirdEyeViewDialog birdeyeDialog;

    JTabbedPane tabPane;
    private List<File> recentFiles = new ArrayList<File>();
    private ActionListener recentFileListener;

    private SessionManager sesMan;
    Map<WorkflowContext, JComponent> projectsTabs = new HashMap<WorkflowContext, JComponent>();
    public static final String SOURCE_AREA_DIV_LOC = "source.area.divloc";


    public static void main(String[] args) throws IOException {

        RFlow rflow = new RFlow();
        rflow.setVisible(true);
        rflow.doAfterSetup();
        rflow.processCmdArgs(args);

//        rflow.setBounds(getPrefs().getInt(".xpos", 100), getPrefs().getInt(".ypos", 100), getPrefs().getInt(".width", 900), getPrefs().getInt(".height", 700));
    }


    public void processCmdArgs(String[] args) {
        if (args.length == 1 && (args[0].equals("-help") || args[0].equals("-h") || args[0].startsWith("--h"))) {
            System.out.println(RFLOW_NAME + " [-f <r-script>]");

            System.exit(0);
        }

        // find the intial file to be loaded
        File preLoadFile = null;
        for (int i = 0; i < args.length; i++) {
            if (args[i].equals("-f")) {
                assert args.length > i : "-f requires a file-argument";
                preLoadFile = new File(args[i + 1]);
                break;
            }
        }


        if (preLoadFile != null && preLoadFile.isFile()) {
            getSesMan().loadWorkflow(preLoadFile);
        }
    }


    public void doAfterSetup() {
        InviteTranslatorsDialog.inviteForTranslation(this);
        UpdateChecker.check4Update(this);
        ExampleData.installExampleData(this);
    }


    public RFlow() {
        initComponents();

        // increment the startup counter
        Utils.getPrefs().putInt(Utils.PROP_STARTUP_COUNTER, Utils.getPrefs().getInt(Utils.PROP_STARTUP_COUNTER, 0) + 1);

        if (!System.getProperty("user.name").equals("brandl"))
            Thread.setDefaultUncaughtExceptionHandler(new GlobalExceptionHandler());


        if (Utils.isMacOSX()) {
            //            setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
        } else {
            setIconImage(new ImageIcon(Utils.loadResource("icons/rflowlogo.png")).getImage());

            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent windowEvent) {
                // saveCatTreeBeforeQuit(RFlow.this);
            }


            @Override
            public void windowActivated(WindowEvent windowEvent) {


                for (WorkflowContext workflowContext : sesMan.getWorkflows()) {
                    workflowContext.syncGraphWithFile();
                }
            }


            @Override
            public void windowDeactivated(WindowEvent windowEvent) {
                if (Utils.getPrefs().getBoolean(GeneralSettings.AUTO_SAVE, GeneralSettings.AUTO_SAVE_DEFAULT))
                    sesMan.processUnsavedChanges();
            }
        });

        // link the menu entries to actions
//        aboutMenuItem.setAction(new AboutAction(this));
//        helpMenuItem.setAction(new HelpAction());
//        flahCardsMenuItem.setAction(new URLAction("Flashcard Repository", "http://www.google.com/search?q=filetype%3Appt"));
//        prefsMenuItem.setAction(new SettingsAction(this));


        // restore the previous position and size of the window
//        Rectangle screenBounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0].getDefaultConfiguration().getBounds();
//        setBounds(LayoutRestorer.getInstance().getBounds("rflow", this, new Rectangle((int) (screenBounds.getWidth() / 2. - 400), (int) (screenBounds.getHeight() / 2. - 350), 800, 700)));


        UIUtils.helpOnF1(this, "help");

        sesMan = new SessionManager(this);

        //        cmLocation = new File("lala.sxl");
        //        cmLocation = new File("../../sphinx4/src/demo/sphinx/hellongram/hellongram.config.xml");

        if (!Utils.isMacOSX()) {
            addWindowListener(new WindowAdapter() {

                public void windowClosing(WindowEvent e) {
                    super.windowClosing(e);
                    exitRFlow();
                }
            });
        }

        addComponentListener(new ComponentAdapter() {

            public void componentResized(ComponentEvent e) {
                handleSizeEvent();
            }


            @Override
            public void componentMoved(ComponentEvent componentEvent) {
                handleSizeEvent();
            }


            private void handleSizeEvent() {
                Utils.saveWindowBounds("mainwin", RFlow.this);
            }
        });

        Utils.restoreWindowBounds("mainwin", RFlow.this);

        recentFileListener = new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                for (int i = 0; i < recentMenu.getItemCount(); i++) {
                    if (recentMenu.getItem(i).equals(e.getSource())) {
                        File recentFile = recentFiles.get(i);
                        if (recentFile.getName().endsWith(SessionManager.FORMAT_SUFFIX)) {
                            sesMan.loadWorkflow(recentFile);
                        } else {
                            assert false : "invaled recent file " + recentFile.getAbsolutePath();
                        }
                    }
                }
            }
        };

        setFocusable(true);

        //        addDummyNodes(scene);
        tabPane = new JTabbedPane();
        tabPane.addMouseListener(new MouseAdapter() {

            public void mouseClicked(MouseEvent e) {
                if (e.getButton() == MouseEvent.BUTTON2)
                    closeTabItemActionPerformed(getTabWorkflow((Component) e.getSource()));
            }
        });

        tabPane.addChangeListener(new ChangeListener() {

            public void stateChanged(ChangeEvent e) {
                Component selectedTab = tabPane.getSelectedComponent();
                WorkflowContext newActiveWorkflow = getTabWorkflow(selectedTab);

                if (newActiveWorkflow != null) {
                    sesMan.setActiveWFContext(newActiveWorkflow);
                    RFlow.this.setTitle(RFLOW_NAME + " - " + newActiveWorkflow.getLocation().getName());
                }
            }
        });

        sesMan.addSesManListener(new SesManListener() {

            public void newActiveScene(WorkflowContext sc) {
                if (birdeyeDialog != null) {
                    birdeyeDialog.setNewView(sc.getWorkflowScene());
                }


                RWorkflowScene workflowScene = sc.getWorkflowScene();
                workflowScene.getView().requestFocusInWindow();
                workflowScene.setSnap2Grid(snap2GridItem.isSelected());

                // if there is a single selected object reselect it in order to show it wihtin the props-panel
                Set<?> selectedObjects = sc.getWorkflowScene().getSelectedObjects();
                if (selectedObjects.size() == 1) {
                    Object selObject = selectedObjects.iterator().next();
                    if (selObject instanceof RBlockNode) {
                        //                        propSheetPanel.rebuildPanel(((ConfNode) selObject).getPropSheet());
                        System.err.println("selected node");
                    }
                }
            }


            public void addedScene(WorkflowContext sc) {
                JScrollPane scrollPane = new JScrollPane();

                RWorkflowScene workflowScene = sc.getWorkflowScene();
                JComponent sceneView = workflowScene.getView();
                sceneView.setFocusable(true);
                sceneView.setEnabled(true);

                workflowScene.setKeyEventProcessingType(EventProcessingType.ALL_WIDGETS);

                scrollPane.setViewportView(workflowScene.getView());

                projectsTabs.put(sc, scrollPane);

                if (sesMan.workflowContexts.size() == 1) {
                    if (!(scenePane.getComponents().length > 0 && scenePane.getComponent(0) instanceof JScrollPane)) {
                        scenePane.removeAll();
                        scenePane.add(scrollPane);
                        RFlow.this.setTitle(RFLOW_NAME + " - " + sc.getLocation().getName());
                    }

                } else {
                    if (scenePane.getComponents().length == 0) {
                        scenePane.add(tabPane);
                    } else if (scenePane.getComponent(0) instanceof JScrollPane) {
                        JScrollPane scrollPaneOld = (JScrollPane) scenePane.getComponent(0);

                        scenePane.removeAll();
                        scenePane.add(tabPane);

                        tabPane.addTab(sesMan.getWorkflows().get(0).getLocation().getName(), scrollPaneOld);
                    }

                    tabPane.addTab(sc.getLocation().getName(), scrollPane);
                    tabPane.validate();
                    tabPane.getModel().setSelectedIndex(tabPane.indexOfComponent(scrollPane));
                }


                scenePane.validate();
            }


            public void removedScene(WorkflowContext sc) {
                if (sesMan.workflowContexts.size() > 0) {
                    tabPane.remove(projectsTabs.get(sc));

                } else {
                    scenePane.removeAll();
                    tabPane.removeAll();

//                    WorkflowContext initalWorkflowContext = new WorkflowContext(null);
//                    sesMan.addWorkflowTab(initalWorkflowContext);
//                    sesMan.setActiveWFContext(initalWorkflowContext);
                }

                tabPane.validate();
                scenePane.validate();
            }
        });


        aboutMenuItem.setAction(new AboutAction(this));
        expSceneImgItem.setAction(new ExportImageAction(sesMan, false, this));

        // intit the view
//        WorkflowContext emptyWorkflowContext = new WorkflowContext(null);
//        sesMan.addWorkflowTab(emptyWorkflowContext);
//        sesMan.setActiveWFContext(emptyWorkflowContext);

        sourceArea.setSessionManager(sesMan);
        statusBar.setSessionManager(sesMan);

        //restore the divider location
        splitPane1.setDividerLocation(Utils.getPrefs().getDouble(SOURCE_AREA_DIV_LOC, 0.8f));

        updateRecentFiles(null);
    }


    private WorkflowContext getTabWorkflow(Component selectedTab) {
        WorkflowContext newActiveWorkflow = null;
        for (WorkflowContext sc : projectsTabs.keySet()) {
            if (projectsTabs.get(sc).equals(selectedTab))
                newActiveWorkflow = sc;
        }
        return newActiveWorkflow;
    }


    public SessionManager getSesMan() {
        return sesMan;
    }


    private void newItemActionPerformed() {
        WorkflowContext newWC = new WorkflowContext(null);
        RWorkflowModel wfModel = newWC.getModel();
        RBlock dummyBlock = new RBlock(wfModel, "");
        dummyBlock.setCode("#### New Block\n\nlibrary(plyr)\n\n # TODO add your code here");
        wfModel.addBlock(dummyBlock);

        sesMan.saveWorkflowAs(newWC);
        File newWorkflowLocation = newWC.getLocation();
        if (newWorkflowLocation != null && newWorkflowLocation.isFile()) {
            sesMan.loadWorkflow(newWorkflowLocation);
        }
    }


    private void showBirdViewItemActionPerformed() {
        showBirdViewItem.setSelected(!showBirdViewItem.isSelected());

        if (birdeyeDialog == null) {
            birdeyeDialog = new BirdEyeViewDialog(this);
        }

        birdeyeDialog.setVisible(showBirdViewItem.isSelected());
    }


    private void layoutGraphItemActionPerformed() {
        sesMan.getActiveWorkflow().getWorkflowScene().layoutScene();
        System.err.println("scene layout updated");
    }


    void saveItemActionPerformed() {
        WorkflowContext tab = sesMan.getActiveWorkflow();
        sesMan.saveWorkflow(tab);
    }


    void saveAsItemActionPerformed() {
        WorkflowContext activeWorkflow = sesMan.getActiveWorkflow();

        sesMan.saveWorkflowAs(activeWorkflow);
    }


    private void loadItemActionPerformed() {
        sesMan.selectWorkflow2Load();
    }


    private void closeTabItemActionPerformed(WorkflowContext tabWorkflow) {
        sesMan.unRegisterSceneContext(tabWorkflow);
    }


    private void exitRFlow() {
        sesMan.closeAll();
        setVisible(false);
        dispose();
    }


    public void setSceneName(WorkflowContext workflow, String newSceneName) {
        if (sesMan.getActiveWorkflow().equals(workflow))
            setTitle(RFlow.RFLOW_NAME + " - " + newSceneName);

        if (tabPane.getComponentCount() > 0)
            tabPane.setTitleAt(tabPane.indexOfComponent(projectsTabs.get(workflow)), newSceneName);
    }


    /**
     * Updates the recent file dialog.
     */
    public void updateRecentFiles(File fileLocation) {
        // get the list
        Preferences p = Utils.getPrefs();
        recentFiles.clear();

        for (int i = 0; i < 5; i++) {
            String path = p.get("recent" + i, null);
            if (path != null && new File(path).isFile()) {
                recentFiles.add(new File(path));
            }
        }

        //update it
        if (fileLocation != null)
            if (recentFiles.isEmpty() || !recentFiles.get(0).equals(fileLocation))
                recentFiles.add(0, fileLocation);


        for (int i = 0; i < recentFiles.size(); i++) {
            File file = recentFiles.get(i);
            p.put("recent" + i, file.getAbsolutePath());
        }

        //update the menu
        recentMenu.removeAll();
        for (File prefFile : recentFiles) {
            JMenuItem menuItem = new JMenuItem(prefFile.getName());
            menuItem.addActionListener(recentFileListener);

            recentMenu.add(menuItem);
        }
    }


    private void snap2GridItemActionPerformed() {
        sesMan.getActiveWorkflow().getWorkflowScene().setSnap2Grid(snap2GridItem.isSelected());
    }


    private void closeTabItemActionPerformed() {
        sesMan.unRegisterSceneContext(sesMan.getActiveWorkflow());
    }


    private void closePrjItemActionPerformed() {
        // TODO add your code here
    }


    private void exitItemActionPerformed() {
        // TODO add your code here
    }


    private void splitPanePropertyChanged() {
        if (splitPane1.getDividerLocation() > 0)
            Utils.getPrefs().putDouble(SOURCE_AREA_DIV_LOC, splitPane1.getDividerLocation() / (double) splitPane1.getHeight());
    }


    private void initComponents() {
        // JFormDesigner - Component initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
        // Generated using JFormDesigner non-commercial license
        menuBar1 = new JMenuBar();
        menu1 = new JMenu();
        newTabItem = new JMenuItem();
        loadItem = new JMenuItem();
        saveTabItem = new JMenuItem();
        saveTabAsItem = new JMenuItem();
        recentMenu = new JMenu();
        closeTabItem = new JMenuItem();
        exitItem = new JMenuItem();
        menu3 = new JMenu();
        snap2GridItem = new JCheckBoxMenuItem();
        showBirdViewItem = new JMenuItem();
        fitViewItem = new JMenuItem();
        layoutGraphItem = new JMenuItem();
        expSceneImgItem = new JMenuItem();
        menu4 = new JMenu();
        helpItem = new JMenuItem();
        aboutMenuItem = new JMenuItem();
        hSpacer1 = new JPanel(null);
        splitPane1 = new JSplitPane();
        scenePane = new JPanel();
        splitPane2 = new JPanel();
        sourceArea = new SourceArea();
        statusBar = new StatusBar();

        //======== this ========
        setTitle("RFlow");
        setIconImage(null);
        Container contentPane = getContentPane();
        contentPane.setLayout(new BorderLayout());

        //======== menuBar1 ========
        {

            //======== menu1 ========
            {
                menu1.setText("File");

                //---- newTabItem ----
                newTabItem.setText("New...");
                newTabItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
                newTabItem.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        newItemActionPerformed();
                    }
                });
                menu1.add(newTabItem);

                //---- loadItem ----
                loadItem.setText("Load...");
                loadItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
                loadItem.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        loadItemActionPerformed();
                    }
                });
                menu1.add(loadItem);

                //---- saveTabItem ----
                saveTabItem.setText("Save");
                saveTabItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
                saveTabItem.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        saveItemActionPerformed();
                    }
                });
                menu1.add(saveTabItem);

                //---- saveTabAsItem ----
                saveTabAsItem.setText("Save As...");
                saveTabAsItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK | KeyEvent.ALT_MASK));
                saveTabAsItem.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        saveAsItemActionPerformed();
                    }
                });
                menu1.add(saveTabAsItem);
                menu1.addSeparator();

                //======== recentMenu ========
                {
                    recentMenu.setText("Open Recent");
                }
                menu1.add(recentMenu);
                menu1.addSeparator();

                //---- closeTabItem ----
                closeTabItem.setText("Close");
                closeTabItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_MASK));
                closeTabItem.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        closeTabItemActionPerformed();
                    }
                });
                menu1.add(closeTabItem);
                menu1.addSeparator();

                //---- exitItem ----
                exitItem.setText("Exit");
                exitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
                exitItem.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        exitItemActionPerformed();
                    }
                });
                menu1.add(exitItem);
            }
            menuBar1.add(menu1);

            //======== menu3 ========
            {
                menu3.setText("View");

                //---- snap2GridItem ----
                snap2GridItem.setText("Snap to Grid");
                snap2GridItem.setSelected(true);
                snap2GridItem.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        snap2GridItemActionPerformed();
                    }
                });
                menu3.add(snap2GridItem);

                //---- showBirdViewItem ----
                showBirdViewItem.setText("Show Birdeye View");
                showBirdViewItem.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        showBirdViewItemActionPerformed();
                    }
                });
                menu3.add(showBirdViewItem);
                menu3.addSeparator();

                //---- fitViewItem ----
                fitViewItem.setText("Fit View");
                fitViewItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.ALT_MASK));
                menu3.add(fitViewItem);

                //---- layoutGraphItem ----
                layoutGraphItem.setText("Relayout Graph");
                layoutGraphItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, KeyEvent.ALT_MASK));
                layoutGraphItem.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        layoutGraphItemActionPerformed();
                    }
                });
                menu3.add(layoutGraphItem);
                menu3.addSeparator();

                //---- expSceneImgItem ----
                expSceneImgItem.setText("Export Scene Image");
                menu3.add(expSceneImgItem);
            }
            menuBar1.add(menu3);

            //======== menu4 ========
            {
                menu4.setText("Help");

                //---- helpItem ----
                helpItem.setText("Help");
                helpItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0));
                menu4.add(helpItem);

                //---- aboutMenuItem ----
                aboutMenuItem.setAction(null);
                aboutMenuItem.setText("About");
                menu4.add(aboutMenuItem);
            }
            menuBar1.add(menu4);
            menuBar1.add(hSpacer1);
        }
        setJMenuBar(menuBar1);

        //======== splitPane1 ========
        {
            splitPane1.setDividerSize(2);
            splitPane1.setResizeWeight(0.5);
            splitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT);
            splitPane1.addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent e) {
                    splitPanePropertyChanged();
                }
            });

            //======== scenePane ========
            {
                scenePane.setPreferredSize(new Dimension(200, 200));
                scenePane.setFocusTraversalPolicyProvider(true);
                scenePane.setLayout(new BorderLayout());
            }
            splitPane1.setTopComponent(scenePane);

            //======== splitPane2 ========
            {
                splitPane2.setLayout(new BorderLayout());

                //---- sourceArea ----
                sourceArea.setPreferredSize(null);
                sourceArea.setMinimumSize(new Dimension(50, 23));
                splitPane2.add(sourceArea, BorderLayout.CENTER);
            }
            splitPane1.setBottomComponent(splitPane2);
        }
        contentPane.add(splitPane1, BorderLayout.CENTER);
        contentPane.add(statusBar, BorderLayout.SOUTH);
        pack();
        setLocationRelativeTo(getOwner());
        // JFormDesigner - End of component initialization  //GEN-END:initComponents
    }


    // JFormDesigner - Variables declaration - DO NOT MODIFY  //GEN-BEGIN:variables
    // Generated using JFormDesigner non-commercial license
    private JMenuBar menuBar1;
    private JMenu menu1;
    private JMenuItem newTabItem;
    private JMenuItem loadItem;
    private JMenuItem saveTabItem;
    private JMenuItem saveTabAsItem;
    private JMenu recentMenu;
    private JMenuItem closeTabItem;
    private JMenuItem exitItem;
    private JMenu menu3;
    private JCheckBoxMenuItem snap2GridItem;
    private JMenuItem showBirdViewItem;
    private JMenuItem fitViewItem;
    private JMenuItem layoutGraphItem;
    private JMenuItem expSceneImgItem;
    private JMenu menu4;
    private JMenuItem helpItem;
    private JMenuItem aboutMenuItem;
    private JPanel hSpacer1;
    private JSplitPane splitPane1;
    private JPanel scenePane;
    private JPanel splitPane2;
    public SourceArea sourceArea;
    private StatusBar statusBar;
    // JFormDesigner - End of variables declaration  //GEN-END:variables


}
